package com.yonyou.ucf.mdf.query.core.service;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.yonyou.iuap.context.InvocationInfoProxy;
import com.yonyou.ucf.mdf.query.core.PartitionContextBuilder;
import com.yonyou.ucf.mdf.query.core.QueryPartitionBuilder;
import com.yonyou.ucf.mdf.query.core.partition.DefaultQueryPartitionBuilder;
import com.yonyou.ypd.bill.basic.spring.YpdSpringContextHolder;
import org.apache.commons.collections4.ListUtils;
import org.imeta.core.lang.BooleanUtils;
import org.imeta.core.model.Entity;
import org.imeta.core.model.Type;
import org.imeta.orm.base.PartitionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class IsvDefaultQueryPartitionBuilder implements QueryPartitionBuilder {
    private static final Logger logger = LoggerFactory.getLogger(DefaultQueryPartitionBuilder.class);
    private static final List<String> systemPartitions = Collections.unmodifiableList(Arrays.asList("ytenantId", "dr"));
    private static final String SHARED_ZERO_TENANT = "SharedZeroTenant";
    private static final String ZERO_TENANT = "0";
    private static final String YTENANT_ID = "ytenant_id";
    private final Cache<String, PartitionContext> cache;

    public IsvDefaultQueryPartitionBuilder() {
        this.cache = CacheBuilder.newBuilder().maximumSize(10000L).expireAfterAccess(300L, TimeUnit.SECONDS).expireAfterWrite(300L, TimeUnit.SECONDS).build();
    }

    public PartitionContext buildPartitionContext(List<Entity> entities) {
        Map<String, PartitionContext> partitionContextMap = new HashMap();
        List<Entity> standardEntities = (List)entities.stream().filter(this::isLocalPartitionSupport).collect(Collectors.toList());
        List<Entity> remoteEntities = ListUtils.subtract(entities, standardEntities);
        standardEntities.forEach((entity) -> {
            PartitionContext var10000 = (PartitionContext)partitionContextMap.put(entity.fullname(), collectLocalPartition(entity));
        });
        this.buildRemotePartition(partitionContextMap, remoteEntities);
        PartitionContext partitionContext = new PartitionContext();
        partitionContextMap.values().forEach(partitionContext::putAll);
        return partitionContext;
    }

    private boolean isLocalPartitionSupport(Entity entity) {
        Set<String> partitions = new HashSet();
        entity.attributes().forEach((attr) -> {
            if (BooleanUtils.b(attr.isPartition()) && !systemPartitions.contains(attr.partitionName()) && !"ytenant_id".equals(attr.columnName())) {
                partitions.add(attr.partitionName());
            }

        });
        return partitions.isEmpty();
    }

    private void buildRemotePartition(Map<String, PartitionContext> partitionContextMap, List<Entity> remoteEntities) {
        remoteEntities.removeIf((entity) -> {
            PartitionContext partition = (PartitionContext)this.cache.getIfPresent(String.format("%s##%s", InvocationInfoProxy.getYhtAccessToken(), entity.fullname()));
            if (partition != null) {
                partitionContextMap.put(entity.fullname(), partition);
            }

            return partition != null;
        });
        Map<String, List<Entity>> customerEntities = (Map)remoteEntities.stream().collect(Collectors.groupingBy(Type::domain));
        customerEntities.forEach((domain, entityList) -> {
            Map<String, PartitionContext> partitions = this.collectRemotePartition(entityList, domain);
            partitions.forEach((fullname, partition) -> {
                this.cache.put(String.format("%s##%s", InvocationInfoProxy.getYhtAccessToken(), fullname), partition);
            });
            partitionContextMap.putAll(partitions);
        });
    }

    private static PartitionContext collectLocalPartition(Entity entity) {
        PartitionContext context = new PartitionContext();
        entity.attributes().forEach((attr) -> {
            if (BooleanUtils.b(attr.isPartition())) {
                if (!"ytenantId".equalsIgnoreCase(attr.partitionName()) && !"ytenant_id".equals(attr.columnName())) {
                    if ("dr".equalsIgnoreCase(attr.partitionName())) {
                        context.put(attr.fullname(), 0);
                    }
                } else {
                    context.put(attr.fullname(), InvocationInfoProxy.getTenantid());
                    if (BooleanUtils.b(entity.get("SharedZeroTenant"))) {
                        context.put(attr.fullname(), new String[]{"0", InvocationInfoProxy.getTenantid()});
                    }
                }
            }

        });
        return context;
    }

    private Map<String, PartitionContext> collectRemotePartition(List<Entity> entities, String domain) {
        InvocationInfoProxy.setExtendAttribute("isvdomain",domain);
        PartitionContextBuilder partitionContextBuilder = YpdSpringContextHolder.getContext().getBean(PartitionContextBuilder.class);
        List fullNames = (List)entities.stream().map(Entity::fullname).collect(Collectors.toList());

        try {
            return partitionContextBuilder.buildPartitionContext(fullNames);
        } catch (Exception var6) {
            logger.error("execption occured on buildPartitionContext from remote call. remote group : {}, msg : {}", domain, var6.getMessage());
            return remotePartitionFailback(entities);
        }
    }

    private static Map<String, PartitionContext> remotePartitionFailback(List<Entity> entities) {
        Map<String, PartitionContext> partitionContexts = new HashMap();
        entities.forEach((entity) -> {
            PartitionContext context = new PartitionContext();
            entity.attributes().forEach((attr) -> {
                if (BooleanUtils.b(attr.isPartition())) {
                    if (!"ytenantId".equalsIgnoreCase(attr.partitionName()) && !"yhtTenant".equalsIgnoreCase(attr.partitionName()) && !"ytenant_id".equals(attr.columnName())) {
                        if ("dr".equalsIgnoreCase(attr.partitionName())) {
                            context.put(attr.fullname(), 0);
                        }
                    } else {
                        context.put(attr.fullname(), InvocationInfoProxy.getTenantid());
                        if (BooleanUtils.b(entity.get("SharedZeroTenant"))) {
                            context.put(attr.fullname(), new String[]{"0", InvocationInfoProxy.getTenantid()});
                        }
                    }
                }

            });
            partitionContexts.put(entity.fullname(), context);
        });
        return partitionContexts;
    }
}
